package com.bolt.support.spring.mvc.resolver;

import com.bolt.common.utils.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import org.springframework.core.MethodParameter;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableArgumentResolver;
import org.springframework.data.web.PageableHandlerMethodArgumentResolverSupport;
import org.springframework.data.web.SortArgumentResolver;
import org.springframework.data.web.SortHandlerMethodArgumentResolver;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.util.ArrayList;
import java.util.List;

public class CustomPageableResolver extends PageableHandlerMethodArgumentResolverSupport
        implements PageableArgumentResolver {


    private static final SortHandlerMethodArgumentResolver DEFAULT_SORT_RESOLVER = new SortHandlerMethodArgumentResolver();
    private SortArgumentResolver sortResolver;

    public CustomPageableResolver() {
        this((SortArgumentResolver) null);
    }


    public CustomPageableResolver(SortHandlerMethodArgumentResolver sortResolver) {
        this((SortArgumentResolver) sortResolver);
    }

    public CustomPageableResolver(@Nullable SortArgumentResolver sortResolver) {
        this.sortResolver = sortResolver == null ? DEFAULT_SORT_RESOLVER : sortResolver;
    }


    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return Pageable.class.equals(parameter.getParameterType());
    }


    @Override
    public Pageable resolveArgument(MethodParameter methodParameter, @Nullable ModelAndViewContainer mavContainer,
                                    NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {
        String pageParamName = getParameterNameToUse(getPageParameterName(), methodParameter);
        String pageSizeParamName = getParameterNameToUse(getSizeParameterName(), methodParameter);
        String page = webRequest.getParameter(pageParamName);
        String pageSize = webRequest.getParameter(pageSizeParamName);
        Sort sort = sortResolver.resolveArgument(methodParameter, mavContainer, webRequest, binderFactory);
        if (StrUtil.isBlank(page) && StrUtil.isBlank(pageSize)) {
            JsonNode body = ResolverUtil.getJsonBody(webRequest);
            if (body.has(pageParamName)) {
                page = body.get(pageParamName).asText();
            }
            if (body.has(pageSizeParamName)) {
                pageSize = body.get(pageSizeParamName).asText();
            }
            if (sort.isUnsorted() && body.has("sort")) {
                JsonNode treeNode = body.get("sort");
                if (treeNode.isArray()) {
                    ArrayNode arrayNode = (ArrayNode) treeNode;
                    List<Sort.Order> orders = new ArrayList<>();
                    for (JsonNode jsonNode : arrayNode) {
                        Sort.Order order = new Sort.Order(Sort.Direction.valueOf(jsonNode.get("direction").textValue()),
                                jsonNode.get("property").textValue());
                        orders.add(order);
                    }
                    sort = Sort.by(orders);
                }
            }
        }


        Pageable pageable = getPageable(methodParameter, page, pageSize);

        if (sort.isSorted()) {
            return PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);
        }

        return pageable;
    }

}